//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "ModelTotalResults.h"
#include "StringUtilities.h"

#include "extern/pugixml/src/pugixml.hpp"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fstream>
#include <iostream>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
using namespace pugi;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define MAX_SIZE 5000000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ModelTotalResults::ModelTotalResults () {

  this->name                              = new String ();
  this->model                             = new String ();
  this->scheme                            = new String ();
  this->selection                         = new String ();
  this->detailsModel                      = new String ();
  this->filenameOut                       = new String ();
  this->filenameIn                        = new String ();
  
  this->detailClassifier                  = new TListE <ModelDetailStatistic> ();
  this->headerClassifier                  = new TListE <String> ();
  this->numberAttributes                  = 0;

  this->toString                          = new String ();

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ModelTotalResults::ModelTotalResults (const ModelTotalResults& modelTotalResults) {

  this->name                              = new String (modelTotalResults.name);
  this->model                             = new String (modelTotalResults.model);
  this->scheme                            = new String (modelTotalResults.scheme);
  this->selection                         = new String (modelTotalResults.selection);
  this->detailsModel                      = new String (modelTotalResults.detailsModel);
  this->filenameOut                       = new String (modelTotalResults.filenameOut);
  this->filenameIn                        = new String (modelTotalResults.filenameIn);
  
  this->detailClassifier                  = new TListE <ModelDetailStatistic> (modelTotalResults.detailClassifier);
  this->headerClassifier                  = new TListE <String> (modelTotalResults.headerClassifier);
  this->numberAttributes                  = modelTotalResults.numberAttributes;

  this->toString                          = new String (modelTotalResults.toString);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ModelTotalResults::ModelTotalResults (const ModelTotalResults* modelTotalResults) {

  this->name                              = new String (modelTotalResults->name);
  this->model                             = new String (modelTotalResults->model);
  this->scheme                            = new String (modelTotalResults->scheme);
  this->selection                         = new String (modelTotalResults->selection);
  this->detailsModel                      = new String (modelTotalResults->detailsModel);
  this->filenameOut                       = new String (modelTotalResults->filenameOut);
  this->filenameIn                        = new String (modelTotalResults->filenameIn);
  
  this->detailClassifier                  = new TListE <ModelDetailStatistic> (modelTotalResults->detailClassifier);
  this->headerClassifier                  = new TListE <String> (modelTotalResults->headerClassifier);
  this->numberAttributes                  = modelTotalResults->numberAttributes;

  this->toString                          = new String (modelTotalResults->toString);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ModelTotalResults::~ModelTotalResults () {

  if (this->name)             delete this->name;
  if (this->model)            delete this->model;
  if (this->scheme)           delete this->scheme;
  if (this->selection)        delete this->selection;
  if (this->detailsModel)     delete this->detailsModel;
  if (this->filenameOut)      delete this->filenameOut;
  if (this->filenameIn)       delete this->filenameIn;
  
  if (this->detailClassifier) delete this->detailClassifier;
  if (this->headerClassifier) delete this->headerClassifier;

  if (this->toString)         delete this->toString;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ModelTotalResults::Name (String* name) {

  if (name)
    *(this->name) = *name;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ModelTotalResults::Model (String* model) {

  if (model)
    *(this->model) = *model;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ModelTotalResults::Scheme (String* scheme) {

  if (scheme)
    *(this->scheme) = *scheme;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ModelTotalResults::Selection (String* selection) {

  if (selection)
    *(this->selection) = *selection;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ModelTotalResults::DetailsModel (String* detailsModel) {

  if (detailsModel)
    *(this->detailsModel) = *detailsModel;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ModelTotalResults::FilenameOut (String* filenameOut) {

  if (filenameOut)
    *(this->filenameOut) = *filenameOut;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ModelTotalResults::FilenameIn (String* filenameIn) {

  if (filenameIn)
    *(this->filenameIn) = *filenameIn;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ModelTotalResults::DetailClassifier (TListE <ModelDetailStatistic>* detailClassifier) {

  if (detailClassifier)
    *(this->detailClassifier) = *detailClassifier;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ModelTotalResults::HeaderClassifier (TListE <String>* headerClassifier) {

  if (headerClassifier)
    *(this->headerClassifier) = *headerClassifier;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ModelTotalResults::NumberAttributes (int numberAttributes) {

  if (numberAttributes > 0)
    this->numberAttributes = numberAttributes;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelTotalResults::Name (void) {

  return this->name;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelTotalResults::Model (void) {

  return this->model;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelTotalResults::Scheme (void) {

  return this->scheme;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelTotalResults::Selection (void) {

  return this->selection;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelTotalResults::DetailsModel (void) {

  return this->detailsModel;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelTotalResults::FilenameOut (void) {

  return this->filenameOut;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelTotalResults::FilenameIn (void) {

  return this->filenameIn;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <ModelDetailStatistic>* ModelTotalResults::DetailClassifier (void) {

  return this->detailClassifier;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <String>* ModelTotalResults::HeaderClassifier (void) {

  return this->headerClassifier;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ModelTotalResults::NumberAttributes (void) {

  return this->numberAttributes;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelTotalResults::ToString (void) {

  char buffer [MAX_SIZE];
  
  this->toString->In("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");

  sprintf(buffer, "<classifier name=\"%s\" scheme=\"%s\" numberattr=\"%d\">\n", this->PrepareAsXMLContent(this->name)->Out(), this->PrepareAsXMLContent(this->scheme)->Out(), this->numberAttributes);
  this->toString->Add(buffer);

  sprintf(buffer, "  <selection>%s</selection>\n", this->PrepareAsXMLContent(this->selection)->Out());
  this->toString->Add(buffer);
  sprintf(buffer, "  <description>%s</description>\n", this->PrepareAsXMLContent(this->detailsModel)->Out());
  this->toString->Add(buffer);

  sprintf(buffer, "  <model>%s  </model>\n", this->PrepareAsXMLContent(this->model)->Out());
  this->toString->Add(buffer);
  sprintf(buffer, "  <statistics>\n");
  this->toString->Add(buffer);

  this->detailClassifier->SetInitial();
  this->headerClassifier->SetInitial();
  while (this->detailClassifier->SetNext() && this->headerClassifier->SetNext()) {
    sprintf(buffer, "    <statistic name=\"%s\">", this->PrepareAsXMLContent(this->headerClassifier->GetCurrent())->Out());
    this->toString->Add(buffer);
    this->toString->Add(this->PrepareAsXMLContent(this->detailClassifier->GetCurrent()->ToString(6))->Out());
    sprintf(buffer, "    </statistic>\n");
    this->toString->Add(buffer);
  }
 
  sprintf(buffer, "  </statistics>\n");
  this->toString->Add(buffer);
  sprintf(buffer, "</classifier>\n");
  this->toString->Add(buffer);

  return this->toString;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ModelTotalResults::LoadFromFile (void) {
  
  if (!this->filenameIn || !this->filenameIn->Length())
    return false;
  
  ifstream file (this->filenameIn->Out());
  
  if (!file.is_open())
    return false;
  
  this->detailClassifier->Clear();
  this->headerClassifier->Clear();
  
  this->detailsModel->In("");
  
  this->numberAttributes = 0;
  
  String testData;
  String trainingData;
  String crossValidation;
  
  bool   loadErrorTestData     = false;
  bool   loadErrorTrainingData = false;
  bool   loadCrossValidation   = false;
  bool   readModel             = false;
  
  char   buffer [MAX_SIZE];
  
  while (!file.eof()) {
    
    file.getline(buffer, MAX_SIZE);
    
    if (!StringUtilities::CountNumberString(buffer))
      continue;
    
    if (strstr(buffer, "Options:")) {
      this->detailsModel->Add(buffer);
      readModel = true;
      this->model->In("");
      continue;
    }
    
    if (strstr(buffer, "* (normalized)"))
      this->numberAttributes++;
    
    if (strstr(buffer, "Time taken to"))
      readModel = false;
    
    if (readModel) { 
      this->model->Add("    ");
      this->model->Add(buffer);
      this->model->Add("\n");
    }
    
    if (strstr(buffer, "Error on test data")) {
      this->headerClassifier->Add(new String("test-data"));
      this->model->In("");
      loadErrorTestData = true;
      continue;
    }
    
    if (strstr(buffer, "Error on training data")) {
      this->headerClassifier->Add(new String("training-data"));
      loadErrorTrainingData = true;
      continue;
    }
    
    if (strstr(buffer, "Stratified cross-validation")) {
      this->headerClassifier->Add(new String("cross-validation"));
      loadCrossValidation   = true;
      loadErrorTrainingData = false;
      continue;
    }
    
    if (loadErrorTestData) {
      testData.Add(buffer);
      testData.Add("\n");
    }
    
    if (loadErrorTrainingData) {
      trainingData.Add(buffer);
      trainingData.Add("\n");
    }
    
    if (loadCrossValidation) {
      crossValidation.Add("      ");
      crossValidation.Add(buffer);
      crossValidation.Add("\n");
    }
    
  }
  
  file.close();

  if (testData.Length()) {
    
    ModelDetailStatistic* newModelDetailStatistic = new ModelDetailStatistic ();
    
    if (newModelDetailStatistic->LoadFromData(&testData))
      this->detailClassifier->Add(newModelDetailStatistic);
    else
      delete newModelDetailStatistic;
    
  }
  
  if (trainingData.Length()) {
    
    ModelDetailStatistic* newModelDetailStatistic = new ModelDetailStatistic ();
    
    if (newModelDetailStatistic->LoadFromData(&trainingData))
      this->detailClassifier->Add(newModelDetailStatistic);
    else
      delete newModelDetailStatistic;
    
  }
  
  if (crossValidation.Length()) {
    
    ModelDetailStatistic* newModelDetailStatistic = new ModelDetailStatistic ();
    
    if (newModelDetailStatistic->LoadFromData(&crossValidation))
      this->detailClassifier->Add(newModelDetailStatistic);
    else
      delete newModelDetailStatistic;
    
  }
  
  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ModelTotalResults::LoadFromXMLFile (void) {

  if (!this->filenameIn || !this->filenameIn->Length())
    return false;
  
  xml_document doc;
  
  if (!doc.load_file(this->filenameIn->Out())) 
    return false;  
  
  this->name->In("");
  this->scheme->In("");
  this->numberAttributes = 0;
  
  this->selection->In("");
  this->detailsModel->In("");
  this->model->In("");
  
  this->detailClassifier->Clear();
  this->headerClassifier->Clear();
  
  xml_node classifier  = doc.child("classifier");
  
  for (xml_attribute attr = classifier.first_attribute(); attr; attr = attr.next_attribute()) {
    if      (!strcmp(attr.name(), "name"))       this->name->In(attr.value()); 
    else if (!strcmp(attr.name(), "scheme"))     this->scheme->In(attr.value()); 
    else if (!strcmp(attr.name(), "numberattr")) this->numberAttributes = atoi(attr.value()); 
  }

  this->selection->In(doc.child("classifier").child_value("selection"));
  this->detailsModel->In(doc.child("classifier").child_value("description"));
  this->model->In(doc.child("classifier").child_value("model"));

  for (xml_node statistic = doc.child("classifier").child("statistics").first_child(); statistic; statistic = statistic.next_sibling()) {

      String                data (statistic.child_value());
      ModelDetailStatistic* newModelDetailStatistic = new ModelDetailStatistic ();

      if (newModelDetailStatistic->LoadFromData(&data)) {
        this->headerClassifier->Add(new String(statistic.attribute("name").value()));
        this->detailClassifier->Add(newModelDetailStatistic);
      }
      else
        delete newModelDetailStatistic;

  }
  
  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ModelTotalResults::LoadFromData (String* data) {
  
  if (!data || !data->Length())
    return false;
  
  this->detailClassifier->Clear();
  this->headerClassifier->Clear();
  
  this->detailsModel->In("");
  
  char*             buffer = strtok(data->Out(), "\n");
  TListE <String>*  lines  = new TListE <String> ();
  
  String            testData;
  String            trainingData;
  String            crossValidation;
  
  bool              loadErrorTestData     = false;
  bool              loadErrorTrainingData = false;
  bool              loadCrossValidation   = false;
  
  while (buffer) {
    lines->Add(new String(buffer));
    buffer = strtok(NULL, "\n");
  }
  
  this->numberAttributes = 0;
  
  bool readModel = false;
  
  lines->SetInitial();
  while (lines->SetNext()) {
    
    if (!StringUtilities::CountNumberString(lines->GetCurrent()->Out()))
      continue;
    
    if (strstr(lines->GetCurrent()->Out(), "Options:")) {
      this->detailsModel->Add(lines->GetCurrent()->Out());
      readModel = true;
      this->model->In("");
    }
    
    if (strstr(lines->GetCurrent()->Out(), "* (normalized)"))
      this->numberAttributes++;
    
    if (strstr(lines->GetCurrent()->Out(), "Time taken to"))
      readModel = false;
    
    if (readModel) 
      this->model->Add(lines->GetCurrent()->Out());
    
    if (strstr(lines->GetCurrent()->Out(), "Error on test data")) {
      this->headerClassifier->Add(new String("test-data"));
      loadErrorTestData = true;
      continue;
    }
    
    if (strstr(lines->GetCurrent()->Out(), "Error on training data")) {
      this->headerClassifier->Add(new String("training-data"));
      loadErrorTrainingData = true;
      continue;
    }
    
    if (strstr(lines->GetCurrent()->Out(), "Stratified cross-validation")) {
      this->headerClassifier->Add(new String("cross-validation"));
      loadCrossValidation   = true;
      loadErrorTrainingData = false;
      continue;
    }
    
    if (loadErrorTestData) 
      testData.Add(lines->GetCurrent()->Out());
    
    if (loadErrorTrainingData) 
      trainingData.Add(lines->GetCurrent()->Out());
    
    if (loadCrossValidation) 
      crossValidation.Add(lines->GetCurrent()->Out());
    
  }
  
  if (lines) 
    delete lines;
  
  if (testData.Length()) {
    
    ModelDetailStatistic* newModelDetailStatistic = new ModelDetailStatistic ();
    
    if (newModelDetailStatistic->LoadFromData(&testData))
      this->detailClassifier->Add(newModelDetailStatistic);
    else
      delete newModelDetailStatistic;
    
  }
  
  if (trainingData.Length()) {
    
    ModelDetailStatistic* newModelDetailStatistic = new ModelDetailStatistic ();
    
    if (newModelDetailStatistic->LoadFromData(&trainingData))
      this->detailClassifier->Add(newModelDetailStatistic);
    else
      delete newModelDetailStatistic;
    
  }
  
  if (crossValidation.Length()) {
    
    ModelDetailStatistic* newModelDetailStatistic = new ModelDetailStatistic ();
    
    if (newModelDetailStatistic->LoadFromData(&crossValidation))
      this->detailClassifier->Add(newModelDetailStatistic);
    else
      delete newModelDetailStatistic;
    
  }
  
  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ModelTotalResults::WriteToFile (void) {

  if (!this->filenameOut || !this->filenameOut->Length())
    return false;
  
  ofstream file(this->filenameOut->Out());
  
  if (!file.is_open())
    return false;
  
  file << this->ToString()->Out() << endl;
  
  file.close();
  
  return true;  

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelTotalResults::PrepareAsXMLContent (String* content) {
  
  if (!content) return NULL;
  
  for (long i = 0; i < content->Length(); i++) {
    if      ((*content)[i] == '<') content->In('{', i);
    else if ((*content)[i] == '>') content->In('}', i);
    else if ((*content)[i] == '&') content->In('^', i);
  }
  
  return content;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ModelTotalResults ModelTotalResults::operator= (const ModelTotalResults& modelTotalResults) {

  *(this->name)             = *(modelTotalResults.name);
  *(this->model)            = *(modelTotalResults.model);
  *(this->scheme)           = *(modelTotalResults.scheme);
  *(this->selection)        = *(modelTotalResults.selection);
  *(this->detailsModel)     = *(modelTotalResults.detailsModel);
  *(this->filenameOut)      = *(modelTotalResults.filenameOut);
  *(this->filenameIn)       = *(modelTotalResults.filenameIn);
  *(this->detailClassifier) = *(modelTotalResults.detailClassifier);
  *(this->headerClassifier) = *(modelTotalResults.headerClassifier);
  
  this->numberAttributes    = modelTotalResults.numberAttributes;

  *(this->toString)         = *(modelTotalResults.toString);

  return *this;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
